package com.yfbao.horizon.schedule.tk.front.service;

import ch.qos.logback.core.util.ExecutorServiceUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.yfbao.horizon.schedule.tk.common.JsonUtils;
import com.yfbao.horizon.schedule.tk.front.dto.FrontLogDto;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Slf4j
@Service
public class FrontendLogService {
    private static final Logger FRONT_LOGGER = LoggerFactory.getLogger("FRONT2_LOG");

    private static final int QUEUE_CAPACITY = 10_000; // adjustable
    private static final int BATCH_SIZE = 500;        // adjustable
    private static final long POLL_TIMEOUT_MS = 100;  // adjustable

    private final BlockingQueue<FrontLogDto> queue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
    private final ExecutorService writerPool = Executors.newFixedThreadPool(1);;
    private volatile boolean running = true;
    private final SeeyonFrontMonitor monitor;



    public FrontendLogService(SeeyonFrontMonitor monitor) {
        this.monitor = monitor;
    }

    public boolean offer(FrontLogDto entry) {
        if (entry == null) {
            return false;
        }
        if (queue.remainingCapacity() < 1) {
            monitor.incrementLogsReceived(false); // 全部拒绝
            return false;
        }
        entry.setEnqueueTimeNanos(System.nanoTime());
        boolean accepted = queue.offer(entry);
        monitor.incrementLogsReceived(accepted);
        updateQueueMetrics();
        return accepted;
    }

    public boolean offerAll(List<FrontLogDto> entries) {
        if (entries == null || entries.isEmpty()) {
            return true;
        }
        // 1. 预检查队列剩余容量
        if (queue.remainingCapacity() < entries.size()) {
            monitor.incrementLogsReceived(false); // 全部拒绝
            return false;
        }
        long now = System.nanoTime();
        entries.forEach(e -> {
            if (e != null) {
                e.setEnqueueTimeNanos(now);
            }
        });
        
        int beforeSize = queue.size();
        boolean accepted = queue.addAll(entries);
        int added = queue.size() - beforeSize;
        
        if (added > 0) {
            monitor.incrementLogsReceived(true);
            if (added < entries.size()) {
                monitor.incrementLogsReceived(false);
            }
            updateQueueMetrics();
        }
        
        return accepted;
    }



    public void logAll(List<FrontLogDto> entries) {
        if (entries == null || entries.isEmpty()) {
            return;
        }
        
        for (FrontLogDto entry : entries) {
            if (entry != null) {
                try {
                    FRONT_LOGGER.info(JsonUtils.toJson(entry));
                    monitor.incrementLogsProcessed(1);
                } catch (Exception e) {
                    log.error("Error logging frontend entry", e);
                    monitor.incrementError("serialization");
                }
            }
        }
    }

    @PostConstruct
    public void start() {
        writerPool.submit(this::writeLoop);
        log.info("Frontend log service started with queue capacity: {}", QUEUE_CAPACITY);
    }

    @PreDestroy
    public void stop() {
        running = false;
        writerPool.shutdown();
        try {
            if (!writerPool.awaitTermination(5, TimeUnit.SECONDS)) {
                writerPool.shutdownNow();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            writerPool.shutdownNow();
        }
        log.info("Frontend log service stopped");
    }

    private void writeLoop() {
        List<FrontLogDto> batch = new ArrayList<>(BATCH_SIZE);
        while (running) {
            try {
                FrontLogDto first = queue.poll(POLL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
                if (first != null) {
                    // 记录队列停留时间
                    long queueTimeNanos = System.nanoTime() - first.getEnqueueTimeNanos();
                    monitor.recordQueueTime(queueTimeNanos);
                    
                    batch.clear();
                    batch.add(first);
                    
                    // 批量处理
                    int drained = queue.drainTo(batch, BATCH_SIZE - 1);
                    monitor.recordBatchSize(batch.size());
                    
                    // 处理批处理
                    long startProcess = System.nanoTime();
                    for (FrontLogDto e : batch) {
                        try {
                            FRONT_LOGGER.info(JsonUtils.toJson(e));
                        } catch (Exception ex) {
                            log.error("Error serializing log entry", ex);
                            monitor.incrementError("serialization");
                        }
                    }
                    
                    // 记录处理延迟
                    long processTimeNanos = System.nanoTime() - startProcess;
                    monitor.recordProcessingTime(processTimeNanos);
                    
                    monitor.incrementLogsProcessed(batch.size());
                    updateQueueMetrics();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            } catch (Throwable t) {
                log.error("Error in frontend log writer loop", t);
                monitor.incrementError("processing");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        
        // 处理队列中剩余的消息
        if (!queue.isEmpty()) {
            log.info("Processing remaining {} log entries before shutdown", queue.size());
            logAll(new ArrayList<>(queue));
            queue.clear();
        }
    }
    
    private void updateQueueMetrics() {
        monitor.updateQueueMetrics(queue.size(), QUEUE_CAPACITY);
    }
}
